
	const UUID_Write = "00000004".toLowerCase();
	const UUID_Notify = "00000005".toLowerCase();
	const UUID_Service = '00000001-cc7a-482a-984a-7f2ed5b3e58f'.toLowerCase();
	const BLE_Name = 'A';
	const FUnit = 200;  //文件上传时每个包的payload大小
	const Min_Interval = 20;  //Simple Send 每条命令最小间隔

	//*************************************   页面控件    ***********************************************
	const log_textarea = document.getElementById('log_textarea')
	const cmdList_textarea = document.getElementById('cmdList_textarea')
	const clearLogBtn = document.getElementById('clearLogBtn')
	const file_textarea = document.getElementById('file_textarea')

	const connectBtn = document.getElementById('connect');
	const disconnectBtn = document.getElementById('disconnect');
	const fileChooseBtn = document.getElementById('fileChooseBtn');
	const file_send = document.getElementById('file_send');
	const uploadBtn = document.getElementById('uploadBtn');
	const upgradeBtn = document.getElementById('upgradeBtn');
	const loopTestBtn = document.getElementById('loopTestBtn');
    const simpleSendBtn = document.getElementById('simpleSendBtn');
	const captureBtn = document.getElementById('captureBtn');
	const showImageBtn = document.getElementById('showImageBtn');
	const hisBtn = document.getElementById('hisBtn');

	const img_camera = document.getElementById( "img_camera" );
	const intervalNum = document.getElementById('intervalNum');
	const cycleNum = document.getElementById('cycleNum');
	const exchangeCmdLog = document.getElementById('exchangeCmdLog');

	const fileVersionInput = document.getElementById('fileVersionInput');
	const fwTypeInput = document.getElementById('fwTypeInput');

	const fileTypeInput = document.getElementById('fileTypeInput');

	const heartDiv = document.getElementById('heart_div')

	function setLog(_log){
		let myDate = new Date().format("MM-dd HH:mm:ss.S");
		log_textarea.innerHTML = log_textarea.innerHTML +'\n'+myDate+" >>> " +_log;
		log_textarea.scroll({ top: log_textarea.scrollHeight, left: 0, behavior: "smooth" });
	}

 

	const loading = document.getElementById('loading');
	const loadingText = document.getElementById('loadingText');
	function showLoading(txt){
		loading.style.display="flex";
		if(txt){
			loadingText.innerHTML = txt;
		}else{
			loadingText.innerHTML = 'Loading';
		}
		// document.body.parentNode.style.overflow= "hidden"
	}

	function closeLoading(){
		loading.style.display="none";
		// document.body.parentNode.style.overflow= "auto"
	}
	
	closeLoading()


	let cmdList_log = document.getElementById('cmdList_log');
	let looplog_1 = document.getElementById('looplog_1');
	let looplog_2 = document.getElementById('looplog_2');
	let looplog_3 = document.getElementById('looplog_3');
	let cmdList_log_clone = cmdList_log.cloneNode(true);
	exchangeCmdLog.onclick = function() {
		if(cmdList_textarea.style.display == "none"){
			cmdList_textarea.style.display = "block";
			cmdList_log.style.display = "none"
		}else {
			cmdList_textarea.style.display = "none";
			cmdList_log.style.display = "block"
		};
	};


	window.removeEventListener("resize",resizeReport,false);
	window.addEventListener("resize",resizeReport,false);
	function resizeReport() {
		var div_img_camera = document.getElementById('div_img_camera');
		var winHeight =  window.screen.width;;//浏览器当前窗口可视区域高度
		img_camera.style.width =  '0px';
		img_camera.style.height =  '0px';
		//先设置为0px，才能保证div_img_camera适配浏览器，然后setTimeout才可以设置img_camera为合适
		setTimeout(() => {
			let divH = div_img_camera.clientHeight;
			let divW = div_img_camera.clientWidth;
			if(divW/divH > 6/4){
				//宽度足够，那以高度为准，设置img_camera宽度
				img_camera.style.height =  0.9*divH +'px';
				img_camera.style.width =  (0.9*divH*6/4) +'px';
			}else{
				img_camera.style.width =  0.9*divW +'px';
				img_camera.style.height =  (0.9*divW * 4/6) +'px';
			}
		}, 50);
	}
	resizeReport();

	//*************************************蓝牙命令相关参数***********************************************
 	var TRANS_ID = 0;	
	const CMD_READ_FW = 0x01;
	const CMD_TAKE_PHOTO = 0x02;
	const CMD_HIS_UUID = 0x03;
	const CMD_PHOTO_INFO = 0x04;
	const CMD_HEART_BEAT = 0x1D;
	const CMD_PHOTO_DOWNLOAD = 0x0A;


	const CMD_FW_UPLOAD_START = 0x11;
	const CMD_FW_UPLOAD = 0x12;
	const CMD_FW_UPLOAD_END = 0x14;
	const CMD_FW_UPGRADE = 0x15;



	// if (navigator.vibrate) {
	// 	[connectBtn, disconnectBtn].forEach(function(element) {
	// 		element.addEventListener('touchstart', function(event) {
	// 			navigator.vibrate(15);
	// 		});
	// 	});
	// }
 

	
	// connectBtn.onclick = function() {
	// 	connect();
	// };

 	// disconnectBtn.onclick = function() {
	// 	disconnect();
	// };

	heartDiv.onclick = function() {
		showLoading();
		send_HEART_BEAT();
	};

	captureBtn.onclick = function() {
		showLoading();
		send_CMD_TAKE_PHOTO();
	};

	showImageBtn.onclick = function() {
		showLoading();
		send_CMD_PHOTO_DOWNLOAD();
	};



	function isNumber(obj){
		if(obj==null) return false;
		if(obj ==undefined) return false;
		return (/(^[1-9]\d*$)/.test(obj))
	}
 
	function resetHistory(){
		
		let no_his_info = document.getElementById('no_his_info');
		no_his_info.style.display="normal";
		//隐藏历史记录
		for(let i = 1; i <= 80; i ++){
			let img = document.getElementById('img_his_'+i);
			img.alt ="";
			img.src ="";
			img.parentElement.style.display="none";

			let infer = document.getElementById('infer_his_'+ i);
			let score = document.getElementById('score_his_'+ i);
			let uuid = document.getElementById('uuid_his_'+ i);
			infer.innerHTML ="Infer:";
			score.innerHTML ="Score:";
			uuid.innerHTML ="UUID : ";
			img.parentElement.parentElement.scrollLeft = 0;
		}
	}
	hisBtn.onclick = function() {
		resetHistory();
		send_CMD_HIS_UUID();
	};



	clearLogBtn.onclick = function() {
		log_textarea.innerHTML = '';
	};



	 
	let gattServer;
	var c_write;
	var device;
	async function connect() {
		log_textarea.innerHTML ='';
		try{
			if (!navigator.bluetooth) {
				setLog('Web Bluetooth API is not available. Please make sure the Web Bluetooth flag is enabled.');
				return;
			}
			setLog('…… Requesting bluetooth device ……');

			//https://developer.chrome.com/articles/bluetooth/#receive_gatt_notifications
	 

			showLoading();
			// const writeCharacteristicId = '00000004-8E22-4541-9D4C-21EDAE82ED19'.toLowerCase();
			// const notifyCharacteristicId = '00000005-8E22-4541-9D4C-21EDAE82ED19'.toLowerCase();
			device = await navigator.bluetooth.requestDevice({
				'filters': [{ 'namePrefix': [BLE_Name] }],
				'optionalServices': [UUID_Service]
			});
			device.addEventListener('gattserverdisconnected', onGattServerDisconnected);//监听设备断开连接

			let server = await device.gatt.connect();
			// setLog('connect');
			// disableDisconnectBtn(false)
			// disableConnectBtn(true)
			// disableGreenBtn(false)
			let service = await server.getPrimaryService(UUID_Service);
			setLog('Got service');
			let services = await server.getPrimaryServices();
			for(let i =0;i < services.length;i++){

				setLog('Got ALL services   >>'+(i+1)+"  "+services[i].uuid);
			}
			let characteristics = await service.getCharacteristics();//获取设备所有信息特征值
			for(let i =0;i < characteristics.length;i++){
				setLog('Got ALL characteristics   >>'+(i+1)+"  "+characteristics[i].uuid);

				if(characteristics[i].uuid.startsWith(UUID_Write)){
					c_write = characteristics[i];
				}else if(characteristics[i].uuid.startsWith(UUID_Notify)){
					try{
						//!!!!!重要!!!!! startNotifications一定要在windows系统蓝牙里面配对成功,否则报authentiction failed  network_error  code:19
						let c_notify = characteristics[i];
						c_notify.startNotifications();//开始监听
						c_notify.addEventListener('characteristicvaluechanged', e => {
								//监听设备端的操作 获取到值之后再解析
								parseNotification(e.target.value.buffer)
								// setLog('Notification:'+JSON.stringify(parseNotification(e.target.value.buffer)));
							}
						);
						setTimeout(() => {
							send_CMD_READ_FW();
						}, 3100);
					}catch(err){
						console.error("connect error:"+err)
						setLog("!!! 重要 !!! 请在windows系统蓝牙里面成功配对,否则 authentiction failed code:19   "+err)
						closeLoading()
						return false;
					}
				}
			}
			return true;
			// closeLoading();
		}catch(err){
			console.error("connect error:"+err)
			setLog("connect error:"+err)
			closeLoading()
			return false;
		}

	};
	
	async function disconnect() {
		try{
			await device.gatt.disconnect();
			return true;
		}catch(error){
			console.log("disconnect:"+error)
			return false;
		}
	}

	function onGattServerDisconnected(event) {
		if(event){
			const device = event.target;
			setLog(`设备: ${device.name} 已经断开连接`);
		}else if(device){
			setLog(`设备: ${device.name} 已经断开连接`);
		}
		closeLoading(); 

		//清除当前拍照
		img_camera.alt = "";
		img_camera.src = "";

		resetHistory();
		document.getElementById('heart_div').style.display ="none";
		document.getElementById('v_hf').innerHTML = '--';
		document.getElementById('v_sf').innerHTML = '--';
		document.getElementById('type_sf').innerHTML = '--';
	}

	// *************************************LOOP TESTING**********************************************
	
	var loopTimes = 1;
	let strLoop;
	var runLoops = 1;
	var cmdList = [];
	var cmdListIdx = 0;
	var isLoopTesting = false;
	var currentLoop;

	const LoopRepTimeout = "--- --- ---";

  function getLoopCmd(){
    let cmd = cmdList_textarea.value.replaceAll(" ","");
    cmd = cmdList_textarea.value.replaceAll("\t","");
    let cmdTmpList = cmd.split("\n")
    cmdTmpList = removeEmptyOfArray(cmdTmpList);
    for(let i = 0 ;i<cmdTmpList.length;i++){
      TRANS_ID = TRANS_ID + 1;
      let cmdArr = cmdTmpList[i].split("_");
      let isFmtErr = false;
      if(cmdArr.length < 2 ){
        isFmtErr = true;
      }
      for(let j = 0 ; j < cmdArr.length;j++){
        if(cmdArr[j] !='' && parseInt("0x"+cmdArr[0], 16) == NaN){
          isFmtErr = true;
        }
      }
      if(isFmtErr){
        cmdList_textarea.value = "！！！<<<Format Error>>> Line:"+(i+1)+"！！！" + "\n" +cmdList_textarea.value;
        return [];
      }
    }
    return cmdTmpList;
  }

  loopTestBtn.onclick = function() {
    let loops = cycleNum.value;
    if (!(/(^[1-9]\d*$)/.test(loops))) { 
      loopTimes = 1;
    }else { 
      loopTimes = parseInt(loops);
    } 
    if(loopTimes > 100) loopTimes = 100;
    cycleNum.value = loopTimes;
    let isOK = prepare_LOOP_TESTING();
    if(isOK) startLoopTest(1, 0);
  };


  simpleSendBtn.onclick = function() {
    let loops = cycleNum.value;
    if (!(/(^[1-9]\d*$)/.test(loops))) { 
      loopTimes = 1;
    }else { 
      loopTimes = parseInt(loops);
    } 
    if(loopTimes > 100) loopTimes = 100;
    cycleNum.value = loopTimes;
  
    let cmdTmpList = getLoopCmd()
    if(cmdTmpList.length == 0){
      return;
    }

    let intervalAll = 0;
    for(let m = 0 ;m<loopTimes;m++){
      for(let i = 0 ;i<cmdTmpList.length;i++){
        TRANS_ID = TRANS_ID + 1;
        let cmdArr = cmdTmpList[i].split("_");
        let payload = [];
        let cmdID = parseInt("0x"+cmdArr[0], 16)
        if(cmdArr[1].length > 0){
          let strArr = every2StrToArray(cmdArr[1]);
          for(let j = 0 ;j<strArr.length;j++){
            payload.push(parseInt("0x"+strArr[j], 16))
          }
        }
  
        let interval = 0;
        if(cmdArr.length == 4 ){
          interval= parseInt(cmdArr[3]);
        }
        if(interval < Min_Interval){
          interval = Min_Interval;
        }
        let bytes = [];
        let pL = payload.length;
        bytes.push(SOF);            //SOF         1byte 
        bytes.push(pL);             //Payload Length  1byte
        bytes.push(cmdID);            //Command ID      1byte
        let arrTransID = int2HexArray(TRANS_ID, 2);
        bytes = bytes.concat(arrTransID);   //Trans ID      2bytes
        bytes.push(0);              //Command Status    1byte
        if(pL > 0){
          bytes = bytes.concat(payload);    //Payload       0~209 bytes
        }
        bytes.push(xorBytes(bytes));      //FCS         1byte
        let packets = new Uint8Array(bytes);
        let currentLoop = {'idx':i,'origin':cmdTmpList[i],'cmdID':cmdID,'transID':TRANS_ID,'payload':payload,'timeout':0,'packets':packets,'response':''}
        
        setTimeout(() => {
			let idxStr = (m+1) +": "+(i+1)+'/'+cmdTmpList.length+''
			showLoading( 'Simple Sending...\n'+ idxStr);
			currentLoop.reqTime =  new Date().format("HH:mm:ss.S");
			console.log(currentLoop.reqTime  +  ">>>  "+ (m+1) +": "+(i+1)+'/'+cmdTmpList.length+'    origin:'+currentLoop.origin+"  packets:"+currentLoop.packets);
			c_write.writeValue(currentLoop.packets).then(() => {
				console.log(idxStr+"  origin:"+currentLoop.origin+"  packets："+currentLoop.packets);
			}); 
        }, intervalAll);

        if(m == loopTimes -1 && i == cmdTmpList.length-1){
          setTimeout(() => {
            closeLoading();
          }, intervalAll);
        }
        intervalAll = intervalAll + interval;
      }
    }
  };

  function prepare_LOOP_TESTING(){
 

    cmdList = [];
    strLoop = "";
    cmdListIdx = 0;
    let cmd = cmdList_textarea.value.replaceAll(" ","");
    cmd = cmdList_textarea.value.replaceAll("\t","");

    let cmdTmpList = getLoopCmd()
    if(cmdTmpList.length == 0){
      return;
    }

	//Log是往cmdList_log不断加span，looplog_1，looplog_2，looplog_3是模板
    //所以每次测试前把cmdList_log清除,并保留最原始的cmdList_log后 clone以下looplog_1，looplog_2，looplog_3
    //写log就是根据clone的looplog_1，looplog_2，looplog_3，追加到cmdList_log
    //为何用这种方式？  就是为了格式化log，有丰富的style
    
	cmdList_textarea.style.display = "none";
    let parentElement = cmdList_log.parentElement;
    parentElement.removeChild(cmdList_log)
    parentElement.appendChild(cmdList_log_clone);
    cmdList_log = cmdList_log_clone;
    cmdList_log_clone = cmdList_log.cloneNode(true);
    setTimeout(() => {
      cmdList_log.style.display = "block"
      looplog_1 =  document.getElementById('looplog_1');
      looplog_2 = document.getElementById('looplog_2');
      looplog_3 = document.getElementById('looplog_3');
    }, 50);


    isLoopTesting = true;

    for(let i = 0 ;i<cmdTmpList.length;i++){
      TRANS_ID = TRANS_ID + 1;
      let cmdArr = cmdTmpList[i].split("_");

      let payload = [];
      let cmdID = parseInt("0x"+cmdArr[0], 16)
      if(cmdArr[1].length > 0){
        let strArr = every2StrToArray(cmdArr[1]);
        for(let j = 0 ;j<strArr.length;j++){
          payload.push(parseInt("0x"+strArr[j], 16))
        }
      }
      let timeout = 0;
      if(cmdArr.length == 3){
        timeout= parseInt( cmdArr[2]);
      }
  
      let interval = 0;
      if(cmdArr.length == 4 ){
        interval= parseInt(cmdArr[3]);
      }
      if(interval < 20){
        interval = 20;
      }
      if(timeout < interval){
        timeout = interval;
      }
      let bytes = [];
      let pL = payload.length;
      bytes.push(SOF);            //SOF         1byte 
      bytes.push(pL);             //Payload Length  1byte
      bytes.push(cmdID);            //Command ID      1byte
      let arrTransID = int2HexArray(TRANS_ID, 2);
      bytes = bytes.concat(arrTransID);   //Trans ID      2bytes
      bytes.push(0);              //Command Status    1byte
      if(pL > 0){
        bytes = bytes.concat(payload);    //Payload       0~209 bytes
      }
      bytes.push(xorBytes(bytes));      //FCS         1byte
      let packets = new Uint8Array(bytes);

      cmdList.push({'idx':i,'origin':cmdTmpList[i],'cmdID':cmdID,'transID':TRANS_ID,'payload':payload,'timeout':timeout,'packets':packets,'response':''})
    }
    return true;
  }
  
  function startLoopTest(loops, idx){
	if(loops == 0 && idx == 0){
		//从头开始执行
		strLoop = "";
	}
	if(loops != runLoops){
		//如果是新一轮
		runLoops = loops;
		for(let i = 0; i < cmdList.length; i ++){
			cmdList[i].loops = runLoops;
		}
	}
	if(idx > 0){
		//idx > 0代表第一条命令已经response或者timeout，进入下一条命令开始send了
		//这时把刚结束的命令的response打印出来
		let tmp =runLoops+"-"+(idx)+'    '+ cmdList[idx -1].origin +"\n    REQ:"+ cmdList[idx -1].reqTime+'  ' + cmdList[idx -1].packets +"\n    REP:"+cmdList[idx -1].repTime+'  ' +  cmdList[idx -1].response;
		strLoop = strLoop + "\n" + tmp;
		let log1 = looplog_1.cloneNode(true);
		let log2 = looplog_2.cloneNode(true);
		let log3 = looplog_3.cloneNode(true);

		log1.innerHTML = runLoops+"-"+(idx)+'&nbsp;&nbsp'+ cmdList[idx -1].origin;
		// log1.id = runLoops+"-"+(idx)+"-1";
 
		log2.innerHTML = "REQ:"+ cmdList[idx -1].reqTime+'&nbsp;&nbsp;' + cmdList[idx -1].packets;
		// log2.id = runLoops+"-"+(idx)+"-2";
	 
		log3.innerHTML = "REP:"+ cmdList[idx -1].repTime+'&nbsp;&nbsp;' + cmdList[idx -1].response;
		// log3.id = runLoops+"-"+(idx)+"-3";

		cmdList_log.appendChild(log1)
		cmdList_log.appendChild(log2)
		cmdList_log.appendChild(log3)
		setTimeout(() => {
			log1.style.display = "block"
			log2.style.display = "block"
			log3.style.display = "block"
			cmdList_log.scrollTop = cmdList_log.scrollHeight;
		}, 10);

		// cmdList_textarea.value = strLoop;
		// cmdList_textarea.scrollTop = cmdList_textarea.scrollHeight;
	}
	
	if(idx >= cmdList.length){
		// getLoopReturnInfo();
		if(runLoops  < loopTimes){
			startLoopTest(runLoops+1, 0);
		}else{
			isLoopTesting = false;
			closeLoading();
			strLoop = "";
		}
		return;
	} 
	if(idx == 0){
		for(let i = 0; i < cmdList.length; i ++){
			cmdList[i].response = '';
		}
	}
	cmdListIdx = idx;
	showLoading('Loop Testing...\n'+ runLoops +": "+(cmdListIdx+1)+'/'+cmdList.length+'');
	currentLoop =  cmdList[idx];
	currentLoop.reqTime =  new Date().format("HH:mm:ss.S");
	if(cmdTaskTimer){
		clearTimeout(cmdTaskTimer);
	}
	cmdTaskTimer = setTimeout(()=>{
		currentLoop.repTime = new Date().format("HH:mm:ss.S") ;
		currentLoop.response = LoopRepTimeout;
		startLoopTest(runLoops, cmdListIdx+1);
	}, currentLoop.timeout);
	currentTransID = currentLoop.transID;
	currentCmdID = currentLoop.cmdID;
	c_write.writeValue(currentLoop.packets).then(() => {
			setLog(runLoops+":"+currentLoop.idx+" CMD:"+currentLoop.origin+"  "+currentLoop.packets);
		}
	);    
}

  function return_LOOP_TESTING(resp,buffer){
    //如果超时后又收到response的话，这时候transID已经不等于当前发送的transID
    //以此判断不能发下一个cmd
    if(currentLoop && cmdList.length > 0){
      //Loop Test 的case
      if(currentLoop.transID == resp.transID){
        for(let i = 0; i < cmdList.length; i ++){
          if(cmdList[i].transID == resp.transID){
            cmdList[i].response = resp.response;
            cmdList[i].repTime = resp.repTime;
            break;
          }
        }
  
        if(cmdTaskTimer){
          clearTimeout(cmdTaskTimer);
        }
        startLoopTest(runLoops, cmdListIdx+1);
      }
    }
  }

 	var currentTransID,currentCmdID;
	let cmdTaskTimer;

	// *************************************READ FW**********************************************
	function send_CMD_READ_FW(){
		document.getElementById('v_hf').innerHTML = '';
		document.getElementById('v_sf').innerHTML = '';
		document.getElementById('type_sf').innerHTML = '';
		showLoading();
		cmdTaskTimer = setTimeout(()=>{
			document.getElementById('v_hf').innerHTML = '--';
			document.getElementById('v_sf').innerHTML = '--';
			document.getElementById('type_sf').innerHTML = '--';
			closeLoading();
		}, 10000);
		let packets = getSendCmd(CMD_READ_FW,[])
		c_write.writeValue(packets).then(() => {
			setLog("CMD_READ_FW:"+packets)
		});				
	}
	function return_CMD_READ_FW(resp,buffer){
		if(cmdTaskTimer){
		  clearTimeout(cmdTaskTimer);
		}
		closeLoading()
		let fromIdx = 0;

		let h_majorV = parseInt("0x"+resp.payload[fromIdx], 16);
		fromIdx = fromIdx + 1;
		let h_midV = parseInt("0x"+resp.payload[fromIdx], 16);
		fromIdx = fromIdx + 1;
		let h_minorV = parseInt("0x"+resp.payload[fromIdx], 16);
		fromIdx = fromIdx + 1;

		//0x00 digit  0x01 round  0x02 fan 0x03 bar    0x04 lamp   0x05 TBD 
		let fwType = parseInt("0x"+resp.payload[fromIdx], 16);
		fromIdx = fromIdx + 1;
 
		console.log(fwType  +  "  :"+h_majorV+"."+h_midV+"."+h_minorV);
		
		let tbrV = ab2str(resp.payloadbuffer.slice(fromIdx,fromIdx+ 1));
		fromIdx = fromIdx + 1;
		let s_majorV = parseInt("0x"+resp.payload[fromIdx], 16);
		fromIdx = fromIdx + 1;
		let s_midV = parseInt("0x"+resp.payload[fromIdx], 16);
		fromIdx = fromIdx + 1;
		let s_minorV = parseInt("0x"+resp.payload[fromIdx], 16);
		fromIdx = fromIdx + 1;
		document.getElementById('v_hf').innerHTML = h_majorV+"."+h_midV+"."+h_minorV;
		document.getElementById('v_sf').innerHTML = tbrV+s_majorV+"."+s_midV+"."+s_minorV;
		document.getElementById('type_sf').innerHTML = fwType == 1?'Round':(fwType == 0?'Digit':(''));
		
	}
	// *************************************TAKE PHOTO**********************************************
	function send_HEART_BEAT(){
		cmdTaskTimer = setTimeout(()=>{
			closeLoading()
		}, 6000);

		let packets = getSendCmd(CMD_HEART_BEAT,[])
		c_write.writeValue(packets).then(() => {
				setLog('CMD_HEART_BEAT write done.'+packets);
			}
		);				
	}
	function return_CMD_HEART_BEAT(resp,buffer){
		if(cmdTaskTimer){
			clearTimeout(cmdTaskTimer);
		}
		closeLoading()
	}


	var currentPhotoInfo;

	// *************************************TAKE PHOTO**********************************************
	function send_CMD_TAKE_PHOTO(){
		cmdTaskTimer = setTimeout(()=>{
			closeLoading()
		}, 10000);

		let packets = getSendCmd(CMD_TAKE_PHOTO,[])
		c_write.writeValue(packets).then(() => {
				setLog('CMD_TAKE_PHOTO write done.'+packets);
			}
		);				
	}

	var imgArr = [];
	function send_CMD_TAKE_PHOTO(){
		cmdTaskTimer = setTimeout(()=>{
			closeLoading()
		}, 10000);

		let packets = getSendCmd(CMD_TAKE_PHOTO,[])
		c_write.writeValue(packets).then(() => {
				setLog('CMD_TAKE_PHOTO write done.'+packets);
			}
		);				
	}


	function return_CMD_TAKE_PHOTO(resp,buffer){
		if(cmdTaskTimer){
			clearTimeout(cmdTaskTimer);
		}
		closeLoading()
		parsePhotoInfo(resp,buffer,0)
		currentPhotoInfo.imgEL = img_camera;
		disableBtn(showImageBtn, false);
	}


	// *************************************History**********************************************
	//1.根据hisUuidsOffset取uuid（send_CMD_HIS_UUID），每次返回10个uuid，继续取（send_CMD_HIS_UUID），取得后存放于hisUuidsArr
	//2.如果取得的uuid数量小于10表示取完了,设置hisPhotoInfoIdx = 0开始取照片信息（send_CMD_PHOTO_INFO），直到hisPhotoInfoIdx=hisUuidsArr.length则取完，照片信息currentPhotoInfo包含packages(照片文件包数量等信息)
	//3.照片信息取完，依据hisUuidsArr下载照片（send_CMD_PHOTO_DOWNLOAD），每张照片有packages个notification包，每个包有offset属性，offset和packages比较可以判断是否下载完成
	function setHisImgAlt(idx,uuid) {
		let img = document.getElementById('img_his_'+idx);
		let uuidEl = document.getElementById('uuid_his_'+idx);
		img.parentElement.style.display="block";
		img.alt = "UUID :  "+uuid;
		uuidEl.innerHTML = "UUID :  "+uuid;
		// img.parentElement.scrollTop = img.parentElement.scrollHeight;
		img.parentElement.parentElement.scrollLeft = img.parentElement.parentElement.scrollWidth;
	}
	// *************************************History UUID**********************************************
	let hisUuidsOffset = 0;
	let hisUuidsArr = [];   //uuid数组，send_CMD_HIS_UUID 返回值
	let hisPhotoInfoArr = [];//Photo Info数组，send_CMD_PHOTO_INFO 返回值,数组长度和hisUuidsArr一致

	let hisPhotoInfoIdx = 0;
	function send_CMD_HIS_UUID(){
		if(hisUuidsOffset == 0){
	 		hisUuidsArr = [];
	 		hisPhotoInfoArr = [];
		}
		showLoading();
		let packets = getSendCmd(CMD_HIS_UUID,[hisUuidsOffset])
		c_write.writeValue(packets).then(() => {
			setLog("CMD_HIS_UUID:"+packets)
			}
		);				
	}

	function return_CMD_HIS_UUID(resp,buffer){
		let cnt = resp.payload.length / 6;   //CMD_HIS_UUID返回的uuid的数量，每个uuid 6byte
		let no_his_info = document.getElementById('no_his_info');
		if(hisUuidsOffset == 0 && cnt == 0){
			//如果第一条CMD_HIS_UUID，返回cnt=0，表示没有history
			closeLoading()
			no_his_info.style.display="normal";
			console.log("warn_no_history")
		}else{
			no_his_info.style.display="none";
			//解析该条命令的全部uuid，并预置hisPhotoInfoArr
			for(let i = 0; i < cnt; i ++){
				let arr = resp.payload.slice(i*6,i*6+6)
				let uuid = arr.join("")
				hisUuidsArr.push(uuid);
				hisPhotoInfoArr.push(null);
				setHisImgAlt(hisUuidsArr.length,uuid);
			}

			//该条命令的全部uuid不足10个，表示所有的取结束了
			//有10个继续下一条
			if(cnt < 10){
				hisUuidsOffset = 0;
				if(cmdTaskTimer){
					clearTimeout(cmdTaskTimer);
				}
				hisPhotoInfoIdx = 0;
				send_CMD_PHOTO_INFO()
			}else{
				hisUuidsOffset = hisUuidsOffset + 10;
				send_CMD_HIS_UUID();
			}
		}
	}



	// *************************************History PHOTO_INFO**********************************************
	function send_CMD_PHOTO_INFO(){
		cmdTaskTimer = setTimeout(()=>{
			closeLoading()
		}, 6000);
		let uuidArr = every2StrToArray(hisUuidsArr[hisPhotoInfoIdx]);
		let payload = [];
		for(let j = 0 ;j<uuidArr.length;j++){
			payload.push(parseInt("0x"+uuidArr[j], 16))
		}
		let packets = getSendCmd(CMD_PHOTO_INFO,payload)
		c_write.writeValue(packets).then(() => {
				setLog('CMD_PHOTO_INFO write done.'+packets);
			}
		);				
	}

	function return_CMD_PHOTO_INFO(resp,buffer){
		if(cmdTaskTimer){
			clearTimeout(cmdTaskTimer);
		}
		parsePhotoInfo(resp,buffer,1)
		currentPhotoInfo.uuid = hisUuidsArr[hisPhotoInfoIdx];
		let img = document.getElementById('img_his_'+ (hisPhotoInfoIdx+1));
		let infer = document.getElementById('infer_his_'+ (hisPhotoInfoIdx+1));
		let score = document.getElementById('score_his_'+ (hisPhotoInfoIdx+1));
		let uuid = document.getElementById('uuid_his_'+ (hisPhotoInfoIdx+1));
		currentPhotoInfo.imgEL = img;
		currentPhotoInfo.hisIdx = hisPhotoInfoIdx;
		img.parentElement.parentElement.scrollLeft = (hisPhotoInfoIdx/hisUuidsArr.length) * img.parentElement.parentElement.scrollWidth;
		img.alt = currentPhotoInfo.uuid;
		infer.innerHTML = "Infer: "+currentPhotoInfo.infers[0].infer;
		score.innerHTML = "Score: "+currentPhotoInfo.infers[0].score;
		uuid.innerHTML = "UUID :  "+currentPhotoInfo.uuid;
		hisPhotoInfoArr[hisPhotoInfoIdx] =  currentPhotoInfo;
		if(hisPhotoInfoIdx < hisUuidsArr.length-1){
			hisPhotoInfoIdx = hisPhotoInfoIdx + 1;
			send_CMD_PHOTO_INFO()
		}else{
			img.parentElement.parentElement.scrollLeft = 0;
			currentPhotoInfo = hisPhotoInfoArr[0];
			send_CMD_PHOTO_DOWNLOAD();
		}
	}
	/**
	 *
	 *  resultFlag:1=history  0=take photo 
	 **/
	function parsePhotoInfo(resp,buffer,resultFlag){
		let fromIdx = 0;
		let uuid = '';
		if(resultFlag == 0){
			for(let i = fromIdx ; i < fromIdx+ 6; i ++ ){
				uuid = uuid + resp.payload[i];
			}
			fromIdx = fromIdx + 6;
		}

		let packages = parseInt("0x"+resp.payload[fromIdx]+resp.payload[fromIdx+1], 16);
		fromIdx = fromIdx + 2;

		imgArr = [];
		for(let i = 0 ; i < packages; i ++ ){
			imgArr.push(null);
		}

		let areas = parseInt("0x"+resp.payload[fromIdx], 16);
		fromIdx = fromIdx + 1;

		let infers = [];
		for(let i = 0 ; i < areas; i ++ ){
			let left = parseInt("0x"+resp.payload[fromIdx]+resp.payload[fromIdx+1], 16);
			fromIdx = fromIdx + 2;

			let top = parseInt("0x"+resp.payload[fromIdx]+resp.payload[fromIdx+1], 16);
			fromIdx = fromIdx + 2;
			
			let right = parseInt("0x"+resp.payload[fromIdx]+resp.payload[fromIdx+1], 16);
			fromIdx = fromIdx + 2;
			
			let bottom = parseInt("0x"+resp.payload[fromIdx]+resp.payload[fromIdx+1], 16);
			fromIdx = fromIdx + 2;

			let inferL = parseInt("0x"+resp.payload[fromIdx], 16);
			fromIdx = fromIdx + 1;

			let infer =  resp.payloadbuffer.slice(fromIdx,fromIdx + inferL)
			fromIdx = fromIdx + inferL;

			let score = parseInt("0x"+resp.payload[fromIdx], 16);
			fromIdx = fromIdx + 1;

			let unit = resp.payloadbuffer.slice(fromIdx,fromIdx+ 12)
			fromIdx = fromIdx + 12;

			infers.push({"left":left,"top":top,"right":right,"bottom":bottom,"score":score,"unit":ab2str(unit,false),"infer":ab2str(infer,false),"inferL":inferL});
		}
		currentPhotoInfo = {"uuid":uuid,"packages":packages,"areas":areas,"infers":infers,"resultFlag":resultFlag};
		console.log("return_CMD_TAKE_PHOTO resp :"+JSON.stringify(currentPhotoInfo));
	}

	// *************************************PHOTO DOWNLOAD**********************************************
	function send_CMD_PHOTO_DOWNLOAD(){
		if(cmdTaskTimer){
			clearTimeout(cmdTaskTimer);
		}
		cmdTaskTimer = setTimeout(()=>{
			closeLoading()
		}, currentPhotoInfo.packages * 500 + 6.0 *1000);

	 	let uuidArr = every2StrToArray(currentPhotoInfo.uuid);
		let payload = [];
		for(let j = 0 ;j<uuidArr.length;j++){
			payload.push(parseInt("0x"+uuidArr[j], 16))
		}
			
		let packets = getSendCmd(CMD_PHOTO_DOWNLOAD, payload)
		setLog("send_CMD_PHOTO_DOWNLOAD:"+packets)
		c_write.writeValue(packets).then(() => {
				// setLog('send_CMD_PHOTO_DOWNLOAD write done.'+packets);
			}
		);	
		//0:拍照取的照片  1:History取的照片
		if(currentPhotoInfo.resultFlag == 1){

		}else if(currentPhotoInfo.resultFlag == 0){
			img_camera.src = "";
			img_camera.alt="Downloading...";	
		}		
	}

	function return_CMD_PHOTO_DOWNLOAD(resp,buffer){
		let uuid = '';
		let fromIdx = 0;
		for(let i = fromIdx ; i < fromIdx+ 6; i ++ ){
			uuid = uuid + resp.payload[i];
		}
		fromIdx = fromIdx + 6;
		let offset = parseInt("0x"+resp.payload[fromIdx], 16);
		fromIdx = fromIdx + 1;

		let data = resp.payloadbuffer.slice(fromIdx)
		imgArr[offset] = data;
		let percent = Math.floor(100.00*(offset+1)/currentPhotoInfo.packages);
		console.log("offset:"+offset+"    packages:"+currentPhotoInfo.packages+"   percent:"+percent)
		currentPhotoInfo.imgEL.alt=currentPhotoInfo.uuid+"\nDownloading...("+percent+"%)";
		if(offset == currentPhotoInfo.packages-1){
			let buffer = unionBufferArray(imgArr);
			let imageUrl = bufferToImgUrl(buffer);
			let img = currentPhotoInfo.imgEL;
	    	img.src = imageUrl;

			if(currentPhotoInfo.resultFlag == 1){
				img.parentElement.parentElement.scrollLeft = (currentPhotoInfo.hisIdx/hisUuidsArr.length) * img.parentElement.parentElement.scrollWidth;
				if(currentPhotoInfo.hisIdx < hisPhotoInfoArr.length-1){
					currentPhotoInfo = hisPhotoInfoArr[currentPhotoInfo.hisIdx+1]
					send_CMD_PHOTO_DOWNLOAD();
				}else{
					closeLoading()
				}
			}else{
		    	// downloadImage(currentPhotoInfo.uuid, img);
				closeLoading()
			}
		}
	}



	// *************************************FILE UPLOAD**********************************************
	let fileArrList = [];
	let FSize = 0;
	// file_send.onchange = async function () {
	// 	fileArrList = chooseFile();
    // }

	// uploadBtn.onclick = function() {
	// 	prepareUploadFile();
	// };
	// upgradeBtn.onclick = function() {
	// 	upgrade();
	// };

 

	async function chooseFile(){
		showLoading();
		try {
			let tmpStr ='';
			let fileName_upload = document.getElementById('fileName_upload');
			var fileObj = file_send.files[0];  
			fileName_upload.innerHTML = fileObj.name;
			fileArrList = await parsefile(file_send, FUnit);
			if(fileArrList.length < 300){
				tmpStr = fileArrList[0];
				for(let i = 1; i < fileArrList.length; i ++){
					tmpStr = tmpStr +"\n" + fileArrList[i];
				}
			}else{
				tmpStr = "File's packages is more than 300, please read it from console by F12";
			 	console.log(fileArrList)
			}
			file_textarea.value = tmpStr;
		} catch (error) {
			console.error("file_send.onchange error:"+error);
		}
		closeLoading();
		return fileArrList;
	}


	
	let fileType = '';
	function prepareUploadFile(){
		if (fileArrList.length == 0) { 
			alert('No valid file choosed!')
			return false;
		} 

		fileType = fileTypeInput.value;		//0 layout  1 output
		if (fileType !='0' && fileType !='1') { 
			alert('File Type is invalid!')
			return false;
		} 

		let fwType = fwTypeInput.value;
		if (!(/(^[0-9]\d*$)/.test(fwType))) { 
			alert('Firmware Type is invalid!')
			return false;
		} 
		
		let fileVersion = fileVersionInput.value;
		let verArr = fileVersion.split(".")
		let isVersionValid = true;
		if(verArr[0] != 'T' && verArr[0] != 'B' && verArr[0] != 'R'){
			isVersionValid = false;
		}else{
			for(let i = 1; i <verArr.length; i ++){
				if (!(/(^[0-9]\d*$)/.test(verArr[i]))) { 
					isVersionValid = false;
					break;
				} 
			}
		}
		if(!isVersionValid){
			alert('File Version is invalid!')
			return false;
		}

		let fileVer = [verArr[0].charCodeAt(0),parseInt(verArr[1]),parseInt(verArr[2]),parseInt(verArr[3])];
		let fileSize = int2HexArray(FSize, 4);
		let pkNum = int2HexArray(fileArrList.length, 2);
		let payload =[parseInt(fwType), ...fileVer,parseInt(fileType),...fileSize,...pkNum];
		console.log("uploadFile:"+payload);
		send_CMD_FW_UPLOAD_START(payload);
	}
	let uploadIdx = 0;
	function uploadFile(idx){
		uploadIdx = idx;
		let pkIdx = int2HexArray(idx, 2);
        var pkSize = fileArrList[idx].length;
        var fwData = fileArrList[idx];
		let payload =[fileType, ...pkIdx,pkSize,...fwData];
		let progress = Math.ceil(100 * (uploadIdx+1) / (fileArrList.length*1.00));
		let ss = idx%3;
		let msg = 'Uploading';
		for(let i = 0 ; i <= ss; i ++){
			msg = msg +".";
		}
		for(let i = ss ; i < 3; i ++){
			msg = msg +" ";
		}
		showLoading(msg + ' '+progress+'%');
		send_CMD_FW_UPLOAD(payload);
	}
	

	function send_CMD_FW_UPLOAD_START(payload){
		showLoading();
		if(cmdTaskTimer){
			clearTimeout(cmdTaskTimer);
		}
		cmdTaskTimer = setTimeout(()=>{
			closeLoading()
		},  6000);
		let packets = getSendCmd(CMD_FW_UPLOAD_START, payload)
		c_write.writeValue(packets).then(() => {
				console.log('send_CMD_FW_UPLOAD_START write done.'+packets);
			}
		);	
	}
	function send_CMD_FW_UPLOAD(payload){
		if(cmdTaskTimer){
			clearTimeout(cmdTaskTimer);
		}
		cmdTaskTimer = setTimeout(()=>{
			closeLoading()
		},  6000);
		let packets = getSendCmd(CMD_FW_UPLOAD, payload)
		c_write.writeValue(packets).then(() => {
				console.log('send_CMD_FW_UPLOAD write done.'+packets);
			}
		);	
	}
	function send_CMD_FW_UPLOAD_END(){
		if(cmdTaskTimer){
			clearTimeout(cmdTaskTimer);
		}
		cmdTaskTimer = setTimeout(()=>{
			closeLoading()
		},  6000);
		let payload =[fileType];
		let packets = getSendCmd(CMD_FW_UPLOAD_END, payload)
		c_write.writeValue(packets).then(() => {
				console.log('send_CMD_FW_UPLOAD_END write done.'+packets);
			}
		);	
	}

	function return_CMD_FW_UPLOAD_START(resp,buffer){
		if (resp.cmdStatus == 0) {
			let retCode = resp.payload[0];
			let index = parseInt("0x"+resp.payload[1]+resp.payload[2], 16);
			if(retCode == 0){
				uploadFile(index);
			}else if(retCode == 1){
				send_CMD_FW_UPLOAD_END();
			}else{
				console.log("return_CMD_FW_UPLOAD_START Error retCode:"+retCode)
			}
		}else{
			console.log("return_CMD_FW_UPLOAD_START Error cmdStatus:"+resp.cmdStatus)
		}
	}

	function return_CMD_FW_UPLOAD(resp,buffer){
		if (resp.cmdStatus == 0) {
			let retCode = resp.payload[0];
			if(retCode == 0){
				if(uploadIdx >= fileArrList.length-1){
					send_CMD_FW_UPLOAD_END();
				}else{
					uploadFile(uploadIdx+1);
				}
			}else{
				console.log("return_CMD_FW_UPLOAD Error retCode:"+retCode)
			}
		}else{
			console.log("return_CMD_FW_UPLOAD Error cmdStatus:"+resp.cmdStatus)
		}
	}

	function return_CMD_FW_UPLOAD_END(resp,buffer){
		closeLoading()
		if (resp.cmdStatus == 0) {
			console.log("return_CMD_FW_UPLOAD_END OK!!!!!")
		}else{
			console.log("return_CMD_FW_UPLOAD_END Error cmdStatus:"+resp.cmdStatus)
		}
	}

	function return_CMD_FW_UPGRADE(resp,buffer){
		if (resp.cmdStatus == 0) {
			console.log("return_CMD_FW_UPGRADE OK!!!!!")
		}else{
			console.log("return_CMD_FW_UPGRADE Error cmdStatus:"+resp.cmdStatus)
		}
	}

	

	// function upgrade(){
	// 	let fileVer = ['T'.charCodeAt(0),2,1,3];
	// 	let fileSize = int2HexArray(FSize, 4);
	// 	var pkNum = int2HexArray(fileArrList.length, 2);
	// 	let payload =[fwType, ...fileVer,fileType,...fileSize,...pkNum]
	// 	console.log("uploadFile:"+payload)
	// 	send_CMD_FW_UPLOAD_START(payload);
	// }
	// function send_CMD_FW_UPGRADE(){
	// 	if(cmdTaskTimer){
	// 		clearTimeout(cmdTaskTimer);
	// 	}
	// 	cmdTaskTimer = setTimeout(()=>{
	// 		closeLoading()
	// 	},  6000);
	// 	let packets = getSendCmd(CMD_FW_UPGRADE, [])
	// 	c_write.writeValue(packets).then(() => {
	// 			console.log('send_CMD_FW_UPGRADE write done.'+packets);
	// 		}
	// 	);	
	// }
	

	/**
	 * 从payload length开始 异或求FCS
	 * bytes: int[] 0:SOF;  1:Payload Length
	 * */
	function xorBytes(bytes){
		let fcs = bytes[1];
		for(let j = 2 ;j < bytes.length;j++){
			fcs = fcs ^ bytes[j];
		}
		return fcs;
	}


	const SOF = 0xFE;
 	// 	SOF		Payload Length	Command ID	Trans ID	Command Status	Payload		FCS
	// 1byte	1byte			1byte		2bytes		1byte			0~209 bytes	1byte
	/**
	 * cmdID:  Command ID  int 
	 * payload:  Payload  int[] 
	 * */
	function getSendCmd(cmdID,payload){
		let bytes = [];
		let pL = payload.length;
		bytes.push(SOF); 						//SOF  				1byte	

		bytes.push(pL);							//Payload Length 	1byte
		bytes.push(cmdID);						//Command ID  		1byte
		TRANS_ID += 1;	
		currentCmdID = cmdID;
		currentTransID = TRANS_ID;
		let arrTransID = int2HexArray(TRANS_ID, 2);
		bytes = bytes.concat(arrTransID);		//Trans ID  		2bytes
		bytes.push(0);							//Command Status  	1byte
		if(payload && pL > 0){
			bytes = bytes.concat(payload);		//Payload  			0~209 bytes
		}
		bytes.push(xorBytes(bytes));			//FCS   			1byte
		return new Uint8Array(bytes);
	}

	function parseNotification(buffer){
		let hexArr = ab2hex(buffer).split(" ");
		let fcs = parseInt("0x"+hexArr[hexArr.length -1], 16);
		let pL = parseInt("0x"+hexArr[1], 16);
		let cmdID = parseInt("0x"+hexArr[2], 16);
		let transID = parseInt("0x"+hexArr[3]+hexArr[4], 16);
		let cmdStatus = parseInt("0x"+hexArr[5], 16);
		let payloadArr = hexArr.slice(6,hexArr.length -1);
		let payloadbuffer = buffer.slice(6,buffer.byteLength -1);
		let resp = {'fcs':fcs,'payloadLen':pL,'cmdID':cmdID,'transID':transID,'cmdStatus':cmdStatus,'payload':payloadArr,'response':hexArr,'payloadbuffer':payloadbuffer,'repTime':new Date().format("HH:mm:ss.S")};
		console.log(JSON.stringify({'cmdID':cmdID,'transID':transID,'cmdStatus':cmdStatus,'response':hexArr}));
		if(isLoopTesting){
			return_LOOP_TESTING(resp,buffer);
		}else{
			if(transID > 0){
				if(transID == currentTransID){
					if(cmdID == CMD_READ_FW){
						return_CMD_READ_FW(resp,buffer);
					}else if(cmdID == CMD_TAKE_PHOTO){
						return_CMD_TAKE_PHOTO(resp,buffer);
					}else if(cmdID == CMD_HEART_BEAT){
						return_CMD_HEART_BEAT(resp,buffer);
					}else if(cmdID == CMD_PHOTO_DOWNLOAD){
						return_CMD_PHOTO_DOWNLOAD(resp,buffer);
					}else if(cmdID == CMD_HIS_UUID){
						return_CMD_HIS_UUID(resp,buffer);
					}else if(cmdID == CMD_PHOTO_INFO){
						return_CMD_PHOTO_INFO(resp,buffer);
					}else if(cmdID == CMD_FW_UPLOAD_START){
						return_CMD_FW_UPLOAD_START(resp,buffer);
					}else if(cmdID == CMD_FW_UPLOAD){
						return_CMD_FW_UPLOAD(resp,buffer);
					}else if(cmdID == CMD_FW_UPLOAD_END){
						return_CMD_FW_UPLOAD_END(resp,buffer);
					}else if(cmdID == CMD_FW_UPGRADE){
						return_CMD_FW_UPGRADE(resp,buffer);
					}else{
		
					}
				}
			}else{
				notify_Remaining_Time(resp,buffer);
			}
		}
		return resp;
	}

	function notify_Remaining_Time (resp,buffer){
		let time = parseInt("0x"+resp.payload[0]+resp.payload[1]+resp.payload[2]+resp.payload[3], 16);
		document.getElementById('heart_time').innerHTML =time;
		let isAnim = document.getElementById('heart_svg').classList.contains("pulse");
		if(!isAnim){
			document.getElementById('heart_div').style.display ="flex";
			document.getElementById('heart_svg').classList.add("pulse");
		}
		if(time <= 5){
			document.getElementById('heart_div').style.display ="none";
		}
	}